package visao;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import logica.Atomo;
import logica.CalculoProposicional;
import logica.InterpretadorDeFormula;
import logica.formula.Formula;
import modelo.FileParser;
import modelo.ModeloProposicional;
import controle.Config;
import controle.Main;

public class JanelaPrincipal extends JFrame implements ActionListener, WindowListener
{
	private static final long serialVersionUID = 1L;
	private JTextField txtFormula;
	private JPanel panel_valores;
	private JButton btnTrocar;
	private JButton btnValorarFrmula;
	private JButton btnTodosParaFalso;
	private JButton btnTodosParaVerdade;
	private JLabel lblStatus;
	
	private JMenuItem mntmLerDadosDe;
	private JMenuItem mntmSalvarDados;
	private JMenuItem mntmSair;
	private JMenuItem mntmLimparFrmula;
	private JMenuItem mntmTrocarFrmula;
	private JMenuItem mntmConfigOperad;
	private JMenuItem mntmConfigVariveis;
	private JMenuItem mntmComoUsar;
	private JMenuItem mntmSobre;
	private JMenuItem mntmValorarFrmula;
	
	//dados de logica
	private Formula formulaAtual;
	private CalculoProposicional calculoProposicionalAtual = new CalculoProposicional();
	private JLabel lblVersao;
	private JTree tree;
	private JMenuItem mntmOpesDoPrograma;
	
	public JanelaPrincipal()
	{
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		setTitle("Programa de Lógica Caqui");
		setIconImage(new ImageIcon("img/caqui_icone.png").getImage());
		setSize(Config.tamanho_tela);
		this.addWindowListener(this);
		
		JTabbedPane painel = new JTabbedPane(JTabbedPane.TOP);
		getContentPane().add(painel, BorderLayout.CENTER);
		
		JPanel panel_formula = new JPanel();
		painel.addTab("Formula", null, panel_formula, null);
		panel_formula.setLayout(new BorderLayout(0, 0));
		
		JPanel panel_formula_label = new JPanel();
		panel_formula.add(panel_formula_label, BorderLayout.NORTH);
		panel_formula_label.setLayout(new BoxLayout(panel_formula_label, BoxLayout.Y_AXIS));
		
		Component verticalStrut = Box.createVerticalStrut(20);
		verticalStrut.setPreferredSize(new Dimension(0, 5));
		panel_formula_label.add(verticalStrut);
		
		JPanel panel = new JPanel();
		panel_formula_label.add(panel);
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
		
		Component horizontalStrut_3 = Box.createHorizontalStrut(20);
		horizontalStrut_3.setPreferredSize(new Dimension(10, 0));
		panel.add(horizontalStrut_3);
		
		JLabel lblFrmula = new JLabel("Fórmula:");
		panel.add(lblFrmula);
		
		Component horizontalGlue_1 = Box.createHorizontalGlue();
		panel.add(horizontalGlue_1);
		
		Component verticalStrut_1 = Box.createVerticalStrut(20);
		verticalStrut_1.setPreferredSize(new Dimension(0, 5));
		panel_formula_label.add(verticalStrut_1);
		
		JPanel panel_formula_field = new JPanel();
		panel_formula_label.add(panel_formula_field);
		panel_formula_field.setLayout(new BoxLayout(panel_formula_field, BoxLayout.X_AXIS));
		
		Component horizontalStrut_2 = Box.createHorizontalStrut(20);
		horizontalStrut_2.setPreferredSize(new Dimension(10, 0));
		panel_formula_field.add(horizontalStrut_2);
		
		txtFormula = new JTextField();
		txtFormula.setAlignmentY(Component.TOP_ALIGNMENT);
		txtFormula.setMaximumSize(new Dimension(2147483647, 25));
		txtFormula.setEditable(false);
		panel_formula_field.add(txtFormula);
		txtFormula.setColumns(10);
		
		btnTrocar = new JButton("Trocar");
		btnTrocar.setAlignmentY(Component.TOP_ALIGNMENT);
		btnTrocar.addActionListener(this);
		
		Component horizontalStrut_1 = Box.createHorizontalStrut(20);
		horizontalStrut_1.setPreferredSize(new Dimension(10, 0));
		panel_formula_field.add(horizontalStrut_1);
		btnTrocar.setPreferredSize(new Dimension(96, 28));
		panel_formula_field.add(btnTrocar);
		
		Component verticalStrut_2 = Box.createVerticalStrut(20);
		verticalStrut_2.setPreferredSize(new Dimension(0, 5));
		panel_formula_label.add(verticalStrut_2);
		
		JPanel panel_arvore = new JPanel();
		panel_formula.add(panel_arvore, BorderLayout.CENTER);
		panel_arvore.setLayout(new BoxLayout(panel_arvore, BoxLayout.Y_AXIS));
		
		Component verticalStrut_3 = Box.createVerticalStrut(20);
		verticalStrut_3.setPreferredSize(new Dimension(0, 5));
		panel_arvore.add(verticalStrut_3);
		
		JLabel lblNewLabel = new JLabel("Árvore de formulas");
		panel_arvore.add(lblNewLabel);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		panel_arvore.add(scrollPane_1);
		
		tree = new JTree(new DefaultTreeModel(new DefaultMutableTreeNode()));
		tree.setBackground(UIManager.getColor("Button.background"));
		scrollPane_1.setViewportView(tree);
		
		JPanel panel_valorar = new JPanel();
		painel.addTab("Valoração", null, panel_valorar, null);
		panel_valorar.setLayout(new BorderLayout(0, 0));
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.getVerticalScrollBar().setUnitIncrement(16);
		panel_valorar.add(scrollPane);
		
		panel_valores = new JPanel();
		panel_valores.setLayout(new BoxLayout(panel_valores, BoxLayout.Y_AXIS));
		scrollPane.setViewportView(panel_valores);
		
		JPanel panel_botoes = new JPanel();
		panel_valorar.add(panel_botoes, BorderLayout.SOUTH);
		
		btnValorarFrmula = new JButton("Valorar fórmula");
		btnValorarFrmula.addActionListener(this);
		panel_botoes.add(btnValorarFrmula);
		
		btnTodosParaFalso = new JButton("Todos para falso");
		btnTodosParaFalso.addActionListener(this);
		panel_botoes.add(btnTodosParaFalso);
		
		btnTodosParaVerdade = new JButton("Todos para verdade");
		btnTodosParaVerdade.addActionListener(this);
		panel_botoes.add(btnTodosParaVerdade);
		
		JPanel panel_status = new JPanel();
		getContentPane().add(panel_status, BorderLayout.SOUTH);
		panel_status.setLayout(new BoxLayout(panel_status, BoxLayout.X_AXIS));
		
		Component rigidArea_4 = Box.createRigidArea(new Dimension(20, 20));
		rigidArea_4.setPreferredSize(new Dimension(10, 30));
		panel_status.add(rigidArea_4);
		
		JPanel panel_5 = new JPanel();
		panel_status.add(panel_5);
		panel_5.setLayout(new BoxLayout(panel_5, BoxLayout.X_AXIS));
		
		lblStatus = new JLabel("Parado");
		panel_5.add(lblStatus);
		
		Component horizontalGlue = Box.createHorizontalGlue();
		panel_5.add(horizontalGlue);
		
		lblVersao = new JLabel(Main.VERSAO);
		lblVersao.setForeground(Color.LIGHT_GRAY);
		panel_5.add(lblVersao);
		
		Component rigidArea_6 = Box.createRigidArea(new Dimension(20, 20));
		getContentPane().add(rigidArea_6, BorderLayout.WEST);
		rigidArea_6.setPreferredSize(new Dimension(5, 20));
		
		Component rigidArea_7 = Box.createRigidArea(new Dimension(20, 20));
		rigidArea_7.setPreferredSize(new Dimension(5, 20));
		getContentPane().add(rigidArea_7, BorderLayout.EAST);
		
		setLocationRelativeTo(null);
		
		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);
		
		JMenu mnArquivo = new JMenu("Arquivo");
		menuBar.add(mnArquivo);
		
		mntmLerDadosDe = new JMenuItem("Ler dados de arquivo");
		mntmLerDadosDe.addActionListener(this);
		mnArquivo.add(mntmLerDadosDe);
		
		mntmSalvarDados = new JMenuItem("Salvar dados");
		mntmSalvarDados.addActionListener(this);
		mnArquivo.add(mntmSalvarDados);
		
		mntmSair = new JMenuItem("Sair");
		mntmSair.addActionListener(this);
		mnArquivo.add(mntmSair);
		
		JMenu mnEditar = new JMenu("Editar");
		menuBar.add(mnEditar);
		
		mntmLimparFrmula = new JMenuItem("Limpar fórmula");
		mntmLimparFrmula.addActionListener(this);
		mnEditar.add(mntmLimparFrmula);
		
		mntmTrocarFrmula = new JMenuItem("Trocar fórmula");
		mntmTrocarFrmula.addActionListener(this);
		mnEditar.add(mntmTrocarFrmula);
		
		mntmValorarFrmula = new JMenuItem("Valorar fórmula");
		mnEditar.add(mntmValorarFrmula);
		mntmValorarFrmula.addActionListener(this);
		
		JSeparator separator = new JSeparator();
		mnEditar.add(separator);
		
		mntmConfigOperad = new JMenuItem("Configurar operadores");
		mntmConfigOperad.addActionListener(this);
		mnEditar.add(mntmConfigOperad);
		
		mntmConfigVariveis = new JMenuItem("Configurar variáveis");
		mntmConfigVariveis.addActionListener(this);
		mnEditar.add(mntmConfigVariveis);
		
		mntmOpesDoPrograma = new JMenuItem("Preferências");
		mntmOpesDoPrograma.addActionListener(this);
		mnEditar.add(mntmOpesDoPrograma);
		
		JMenu mnAjuda = new JMenu("Ajuda");
		menuBar.add(mnAjuda);
		
		mntmComoUsar = new JMenuItem("Como usar");
		mntmComoUsar.setEnabled(false);
		mntmComoUsar.addActionListener(this);
		mnAjuda.add(mntmComoUsar);
		
		mntmSobre = new JMenuItem("Sobre");
		mntmSobre.addActionListener(this);
		mnAjuda.add(mntmSobre);
		
		atualizaCheckboxes(false);
		setVisible(true);
	}

	@Override
	public void actionPerformed(ActionEvent arg0) 
	{
		Object source = arg0.getSource();
		
		if(source == null)
			return;
		
		else if(source == btnTrocar || source == mntmTrocarFrmula)
		{
			trocarFormula();
		}
		
		else if(source == btnTodosParaFalso)
		{
			atualizaCheckboxes(false);
		}
		
		else if(source == btnTodosParaVerdade)
		{
			atualizaCheckboxes(true);
		}
		
		else if(source == btnValorarFrmula || source == mntmValorarFrmula)
		{
			valorarFormula();
		}
		
		else if(source == mntmSair)
		{
			this.dispose();
		}
		
		else if(source == mntmLimparFrmula)
		{
			txtFormula.setText("");
			formulaAtual = null;
		}
		
		else if(source == mntmSalvarDados)
		{
			salvarDadosNoAquivo();
		}
		
		else if(source == mntmLerDadosDe)
		{
			lerArquivo();
		}
		
		else if(source == mntmOpesDoPrograma)
		{
			ModeloProposicional m = new ModeloProposicional(calculoProposicionalAtual, formulaAtual, pegaValoracaoDosCheckboxes());
			new JanelaConfigurar(this, m, 0).setVisible(true);
			this.calculoProposicionalAtual = m.calculo;
			atualizaCheckboxes(false);
		}
		
		else if(source == mntmConfigVariveis)
		{
			ModeloProposicional m = new ModeloProposicional(calculoProposicionalAtual, formulaAtual, pegaValoracaoDosCheckboxes());
			new JanelaConfigurar(this, m, 1).setVisible(true);
			this.calculoProposicionalAtual = m.calculo;
			atualizaCheckboxes(false);
		}
		
		else if(source == mntmConfigOperad)
		{
			ModeloProposicional m = new ModeloProposicional(calculoProposicionalAtual, formulaAtual, pegaValoracaoDosCheckboxes());
			new JanelaConfigurar(this, m, 2).setVisible(true);
			this.calculoProposicionalAtual = m.calculo;
			atualizaCheckboxes(false);
		}
		
		else if(source == mntmSobre)
		{
			String texto="\nPrograma de Lógica Caqui \n"
					+ "por Carlos F. M. Faruolo \n"
					+ "<5carlosfelipe5@gmail.com> \n\n"
					+ "Versão "+Main.VERSAO;
			JOptionPane.showMessageDialog(this, texto, "Sobre Caqui", JOptionPane.INFORMATION_MESSAGE, new ImageIcon("img/caqui_sobre.png"));
		}
		
	}
	
	/** Cria checkboxes correspondentes aos simbolos proposicionais. Se opcao for true, os checkboxes são criados já marcados. */
	private void atualizaCheckboxes(boolean opcao)
	{
		panel_valores.removeAll();
		
		//queremos uma lista ordenada, para não ficar "feio"
		List<Atomo> lista = new ArrayList<Atomo>(calculoProposicionalAtual.simbolosProposicionais);
		Collections.sort(lista);
		
		for(Atomo c : lista)
		{
			panel_valores.add(new JCheckBox(c.rotulo+" verdadeiro", opcao));
		}
		panel_valores.validate();
	}
	
	private Map<Atomo, Boolean> pegaValoracaoDosCheckboxes()
	{
		Map<Atomo, Boolean> valores = new HashMap<Atomo, Boolean>();
		for(Component c : panel_valores.getComponents())
		{
			JCheckBox check = (JCheckBox) c;
			valores.put(new Atomo(check.getText().charAt(0)), check.isSelected());
		}
		return valores;
	}
	
	private void trocarFormula()
	{
		String expressao;
		expressao = JOptionPane.showInputDialog(this, "Entre com a formula nova", txtFormula.getText());
		if(expressao != null)
		{
			if(expressao.trim().equals(""))
			{
				JOptionPane.showMessageDialog(this, "Campo vazio! Digite uma formula", "Campo vazio", JOptionPane.WARNING_MESSAGE);
			}
			try
			{
				lblStatus.setText("Interpretando...");
				InterpretadorDeFormula interprete = new InterpretadorDeFormula(calculoProposicionalAtual);
				formulaAtual = interprete.interpretar(expressao.trim());
				JOptionPane.showMessageDialog(this,"O interpretador reconheceu a expressao \""+expressao+"\" com sucesso!", "Sucesso!", JOptionPane.INFORMATION_MESSAGE);
//				System.out.println(f.toString());
				txtFormula.setText(expressao.trim());
				
				tree.setModel(new DefaultTreeModel(formulaAtual.toNode()));
				
				lblStatus.setText("Parado");
			}
			catch(Exception e)
			{
				JOptionPane.showMessageDialog(this, e.getMessage()+"\n Tente novamente!", "Erro!", JOptionPane.ERROR_MESSAGE);
			}
		}
	}
	
	private void valorarFormula()
	{
		if(formulaAtual == null)
		{
			trocarFormula();
			if(formulaAtual == null)
				return;
		}
			
		lblStatus.setText("Valorando fórmula...");
		Map<Atomo, Boolean> valores = pegaValoracaoDosCheckboxes();
		
		try 
		{
			boolean resultado = formulaAtual.valorar(valores);
			if(resultado == true)
				JOptionPane.showMessageDialog(this, "Verdade");
			else
				JOptionPane.showMessageDialog(this, "Falso");
		} 
		catch (Exception e) 
		{
			JOptionPane.showMessageDialog(this, "Erro ao valorar fórmula. "+e.getLocalizedMessage(), "Erro", JOptionPane.ERROR_MESSAGE);
		}
		lblStatus.setText("Parado");
	}
	
	private File escolheArquivo(boolean paraSalvar)
	{
		JFileChooser colhedor = new JFileChooser();
		colhedor.setMultiSelectionEnabled(false);
		if(paraSalvar)
			colhedor.showOpenDialog(this);
		else
			colhedor.showSaveDialog(this);
		
		return colhedor.getSelectedFile();
	}
	
	private File escolheArquivoParaSalvar()
	{
		return escolheArquivo(true);
	}
	
	private File escolheArquivoParaAbrir()
	{
		return escolheArquivo(false);
	}
	
	private void lerArquivo()
	{
		File arquivoParaLer = escolheArquivoParaAbrir();
		if(arquivoParaLer==null)
			return;
		
		try 
		{
			ModeloProposicional m = FileParser.parse(arquivoParaLer);
			calculoProposicionalAtual = m.calculo;
			formulaAtual = m.formula; txtFormula.setText(formulaAtual.toString());
			tree.setModel(new DefaultTreeModel(formulaAtual.toNode()));
			panel_valores.removeAll();
			
			//queremos uma lista ordenada, para não ficar "feio"
			List<Atomo> lista = new ArrayList<Atomo>(calculoProposicionalAtual.simbolosProposicionais);
			Collections.sort(lista);
			
			for(Atomo a : lista)
			{
				panel_valores.add(new JCheckBox(a.rotulo+" verdadeiro", m.valoracao.get(a)));
			}
			panel_valores.validate();
			JOptionPane.showMessageDialog(this, "Lido com sucesso.", "Lido!", JOptionPane.INFORMATION_MESSAGE);
		} 
		catch (Exception e) 
		{
			JOptionPane.showMessageDialog(this, e.getLocalizedMessage(), "Erro ao ler arquivo", JOptionPane.ERROR_MESSAGE);
		}
	}
	
	private 
	void salvarDadosNoAquivo()
	{
		File arquivoParaSalvar = escolheArquivoParaSalvar();
		
		if(arquivoParaSalvar==null)
			return;
		
		ModeloProposicional m = new ModeloProposicional(calculoProposicionalAtual, formulaAtual, pegaValoracaoDosCheckboxes());
		try 
		{
			FileParser.save(m, arquivoParaSalvar);
			JOptionPane.showMessageDialog(this, "Salvo com sucesso.", "Salvo!", JOptionPane.INFORMATION_MESSAGE);
		} 
		catch (IOException e) 
		{
			JOptionPane.showMessageDialog(this, e.getLocalizedMessage(), "Erro ao salvar arquivo", JOptionPane.ERROR_MESSAGE);
		}
	}

	@Override
	public void windowActivated(WindowEvent e) {	}
	@Override
	public void windowClosed(WindowEvent e) 
	{
		try 
		{
			Config.salva();
		} 
		catch (Exception e1) 
		{
			JOptionPane.showMessageDialog(this, "Erro ao salvar preferências. "+e1.getLocalizedMessage(), "Aviso", JOptionPane.WARNING_MESSAGE);
		}
	}
	
	@Override
	public void windowClosing(WindowEvent e) 
	{
		if(Config.perguntaParaSair)
			if(JOptionPane.showConfirmDialog(this, "Deseja mesmo sair?", "Pergunta", JOptionPane.YES_NO_OPTION)==JOptionPane.NO_OPTION)
				return;
		
		Config.tamanho_tela = this.getSize();
		dispose();
	}
	
	@Override
	public void windowDeactivated(WindowEvent e) {	}
	@Override
	public void windowDeiconified(WindowEvent e) {	}
	@Override
	public void windowIconified(WindowEvent e) {	}
	@Override
	public void windowOpened(WindowEvent e) {	}

}
